/**
 * Created by george on 9/30/16.
 */

var Question = mongoose.model('question');
var toolUtil = require('../common/utils/toolUtil');
var questionPraise = mongoose.model('question_praise');
var async = require('async');
var geolib = require('geolib');
var Users = mongoose.model('user');

var questionService = require('./questionService');
var wxmutualAidService = require('./wxmutualAidService');

/*
 --- TODO: check if we are using Mongo connection pooling
 */

/**
 * 我的朋友圈发布 - 个人相册
 * @param user
 * @param ts
 * @param direction
 * @param callback
 */
exports.getWXMyJournalsService = function (user, ts, direction, callback) {

    var opt = {
        isValid: true,
        community: user.community._id,
        createdBy: user._id,
        category: {$in: ['share', 'welcome']}

        // $or: [{category: 'share', isShow: null},
        //     {category: 'welcome', isShow: null},
        //     {isShow: -1, createdBy: user._id}]  // 屏蔽掉的
    };

    if (direction == 'top') {
        opt.dateCreated = {$gt: new Date(Number(ts))}
    } else if (direction == 'down') {
        opt.dateCreated = {$lt: Number(ts)}
    }

    if (ts == '0') {
        delete  opt.dateCreated;
    }

    exports.getJournals(opt, user, ts, direction, callback);

};

/**
 * 朋友圈 - 本小区
 * @param user
 * @param ts
 * @param direction
 * @param callback
 */
exports.getWXJournalsService = function (user, ts, direction, callback) {

    var opt = {
        isValid: true,
        community: user.community._id,
        // createdBy: user._id,
        category: {$in: ['share', 'welcome']},
        $or: [{isShow: null}, {isShow: -1, createdBy: user._id}]  // 屏蔽掉的
    };

    if (direction == 'top') {
        opt.dateCreated = {$gt: new Date(Number(ts))}
    } else if (direction == 'down') {
        opt.dateCreated = {$lt: Number(ts)}
    }

    if (ts == '0') {
        delete  opt.dateCreated;
    }

    exports.getJournals(opt, user, ts, direction, callback);
};

/**
 * Internal
 * @param opt
 * @param user
 * @param ts
 * @param direction
 * @param callback
 */
exports.getJournals = function (opt, user, ts, direction, callback) {

    Question.find(opt, 'praiseCount _id desc categoryOrder community replies score ' +
            'category status images replies dateCreated createdBy topicId fromFlag level')
        .populate('createdBy', '_id nickName images')
        // .populate('replies.repliedBy', '_id nickName images userType authenticationStatus showUnit roomUnit userRole')
        .populate('replies.repliedBy', '_id nickName images userType authenticationStatus')
        .populate('replies.replyTo', '_id nickName images')
        .populate('replies.community', 'communityName')
        .sort({dateCreated: -1}).limit(listPageSize)
        .exec(function (err, results) {

            if (err) {
                callback(err, null)
            } else {

                wxmutualAidService.questionsHandle(results, user, function (err, results) {
                    var data = {};

                    //下拉刷新返回数据
                    if (direction == 'top' && results.length > 0) {

                        if (ts != '0') {
                            var count = results.length < listPageSize ? results.length : results.length + Math.ceil(Math.random() * 5);
                            data.refreshMsg = '更新了' + count + '条帖子';
                        }

                        //返回最新时间戳
                        data.latestTs = results[0].dateCreated.getTime();
                    }

                    //新帖子数量超过一页返回下一页时间戳
                    if (results.length >= listPageSize) {
                        data.nextTs = results[results.length - 1].dateCreated.getTime();
                    }

                    // sort by dateReplied 貌似不需要, 本来就是按时间正排的
                    // results.map(function (q) {
                    //     if (q.replies)
                    //         q.replies.sort(function (r1, r2) {
                    //             return r1.dateReplied - r2.dateReplied;
                    //         });
                    // });

                    data.list = results;

                    callback(null, data);

                });
            }
        });
};

/**
 * 他人页 - 4张图片
 * @param user
 * @param callback
 */
exports.getJournalsPicsService = function (user, callback) {

    var uid = mongoose.Types.ObjectId(user._id);
    var cid = mongoose.Types.ObjectId(user.community._id);

    // we can use aggregate as well, and see which is faster.
    Question.find(
        {
            'images.isValid': true,
            'images.imageURL': {$ne: null},
            isValid: true,
            community: cid,
            createdBy: uid,
            category: {$in: ['share', 'welcome']}
        }, '_id images')
        .sort({dateCreated: -1}).limit(4)
        .exec(function (err, result) {
            if (err) logger.error(err.message);
            if (result == null || result.length == 0) {
                callback(err, null);
            } else {

                var images = [];

                result.map(function (q) {
                    images = images.concat(q.images);
                });

                images.splice(4);

                callback(null, images);
            }
        });
};

exports.getJournalsNotificationService = function (user, ts, callback) {

    if (ts == '0') {
        callback(null, {nextTs: new Date().getTime()});
        return;
    }

    async.parallel([
            function (cb) {
                exports.getJournalsCommunityNotificationService(user, ts, cb);
            },
            function (cb) {
                exports.getJournalsUserNotificationService(user, ts, cb);
            }
        ], function (err, result) {

            if (err || result == null || result.length == 0) {
                callback(err, null);
            } else {
                var data = {
                    nextTs: Number(ts),
                    community: [],
                    mine: []
                };

                data.nextTs = (result[0].nextTs < result[1].nextTs) ? result[1].nextTs : result[0].nextTs;
                data.community = result[0];
                data.mine = result[1];

                callback(null, data);
            }
        }
    );
};

/**
 * 朋友圈 - user参与的number of latest replies & last replier
 * @param user
 * @param ts
 * @param callback
 */
exports.getJournalsUserNotificationService = function (user, ts, callback) {
    var uid = mongoose.Types.ObjectId(user._id);

    async.parallel([
            function (cb) {
                // ts = (ts == '0') ? new Date() : new Date(Number(ts));
                var matchOpt = {
                    isValid: true,
                    // community: cid,  // TODO: remove ?
                    dateLastModified: {$gt: new Date(Number(ts))},
                    category: {$in: ['share', 'welcome']},
                    $or: [{createdBy: uid}, {'replies.repliedBy': uid}]
                };

                exports.getLatestReplyOfMyParticipatedService(user, ts, matchOpt, cb);
            },
            function (cb) {
                exports.getLatestLikesOfMyParticipatedService(user, ts, cb);
            },
            function (cb) {
                exports.getLatestLikesOfMyLikedService(user, ts, cb);
            },
            function (cb) {
                exports.getLatestReplyOfMyLikedService(user, ts, cb);
            }
        ], function (err, result) {

            if (err || result == null || result.length == 0) {
                callback(err, null);
            } else {
                var data = {
                    count: 0,
                    nextTs: Number(ts),
                    data: []
                };

                // console.log('getJournalsUserNotificationService', result);

                for (var i = 0; i < result.length; i++) {
                    if (result[i] == null) continue;

                    console.log('i = ', i);
                    console.log('nextTs: ', result[i].nextTs);
                    if (data.nextTs < result[i].nextTs) {
                        data.nextTs = result[i].nextTs;
                        data.lastBy = result[i].data.lastBy;
                    }
                    if (result[i].data)
                        data.data = data.data.concat(result[i].data);
                }

                // eliminate dups
                data.data = data.data.slice()
                    .sort(function (a, b) {
                        return (a._id > b._id) ? 1 : (b._id > a._id) ? -1 : 0;
                    })
                    .reduce(function (a, b) {
                        if (a.length == 0 || !a.slice(-1)[0]._id.equals(b._id)) a.push(b);
                        return a;
                    }, []);

                data.count = data.data.length;

                callback(null, data);
            }
        }
    );
};

/**
 * 朋友圈 - 小区最新发布数, 发布人
 * TODO: this might NOT be right, do we need most recently posted msgs or replies?
 * @param user
 * @param ts
 * @param callback
 */
exports.getJournalsCommunityNotificationService = function (user, ts, callback) {

    Question.find({
            isValid: true,
            community: user.community._id,
            category: {$in: ['share', 'welcome']},
            dateCreated: {$gt: new Date(Number(ts))},
            createdBy: {$ne: user._id}
        }, '_id createdBy dateCreated')
        .sort('-dateCreated')
        .populate('createdBy', 'nickName images userType authenticationStatus')
        .exec(function (err, results) {
            if (err) {
                callback(err, null);
            } else {

                var data = {
                    count: 0,
                    nextTs: Number(ts)
                };

                if (results && results.length > 0) {
                    data.count = results.length;
                    data.lastBy = results[0].createdBy;
                    data.nextTs = results[0].dateCreated.getTime();
                }

                callback(null, data);
            }
        });
};

/**
 * 朋友圈notification
 *  1. # of replies after ts
 *  2. user of the latest reply
 * @param user
 * @param ts
 * @param matchOpt
 * @param callback
 */
exports.getLatestReplyOfMyParticipatedService = function (user, ts, matchOpt, callback) {
    var uid = mongoose.Types.ObjectId(user._id);

    console.log("getLatestReplyOfMyParticipatedService", uid);
    console.log("getLatestReplyOfMyParticipatedService", ts);
    console.log("getLatestReplyOfMyParticipatedService", matchOpt);

    // 参与的是否有回复
    // TODO: maybe should be limited to a period of time
     Question.aggregate([
        {
            $match: matchOpt
        },
        {
            $project: {
                createdBy: 1,
                replies: 1,
                images: 1,
                dateLastModified: 1,
                desc: 1
            }
        },
        {
            $unwind: '$replies'
        },
        {
            $match: {
                'replies.isValid': true,
                // TODO: might NOT be right here, case being users migh have replies several times
                // and one of the later replies came after the ts, and it will be considered as the first reply.
                // hence wrong.
                'replies.dateReplied': {$gt: new Date(Number(ts))}
            }
        },
        {
            $group: {
                _id: '$_id',
                replies: {$push: '$replies'},
                images: {$last: '$images'},
                desc: {$last: '$desc'},
                createdBy: {$last: '$createdBy'}
            }
        },
        // this is to ensure we get the nextTs right.
        {
            $sort: {dateLastModified: -1}
        },
    ]).exec(function (err, result) {
        if (err) logger.error(err.message);
        if (err || result == null) {
            callback(err, null);
        } else {
            Users.populate(result, {
                path: 'replies.repliedBy',
                select: 'nickName images userType authenticationStatus'
            }, function (err, populatedResult) {
                if (err) {
                    callback(err, null);
                } else {
                    var data = {
                        count: 0,
                        nextTs: Number(ts),
                        data: []
                    };

                    console.log('result:', result.length);
                    // console.log('populatedResult:', populatedResult.length);
                    // if (populatedResult.length)
                    //     console.log('populatedResult:', populatedResult[0]);

                    var myPosts = [];
                    var participated = populatedResult.filter(function (q) {
                        if (q.createdBy == user._id) {
                            myPosts = myPosts.concat(q);
                            return false;
                        }

                        var otherReplies = [];

                        // locate my first reply, and remove anything before that
                        var i = 0;
                        for (; i < q.replies.length; i++) {
                            if (q.replies[i].repliedBy._id == user._id) {
                                break;
                            }
                        }

                        console.log("q.replies.length: ", q.replies.length);
                        console.log("idx: ", i);
                        if (++i < q.replies.length)
                            otherReplies = q.replies.slice(i);
                        else {
                            otherReplies = q.replies;
                        }

                        // filter out my own replies, only keep others' replies
                        q.replies = otherReplies.filter(function (r) {
                            return r.repliedBy._id != user._id
                        });
                        // console.log("q:", q);
                        console.log("remaining others' replies: ", q.replies.length);

                        return q.replies.length > 0;
                    });

                    console.log('myPosts:', myPosts.length);
                    console.log('participated:', participated.length);

                    myPosts.concat(participated).map(function (q) {
                        q.replies.map(function (r) {
                            if (r.repliedBy._id != user._id) {
                                var p = {
                                    action: 'reply',
                                    _id: r._id,
                                    dateCreated: r.dateReplied,
                                    desc: r.replyDesc,
                                    user: r.repliedBy,
                                    question: {
                                        _id: q._id
                                    }
                                };

                                if (q.images && q.images.length > 0)
                                    p.question.images = q.images[0];
                                else
                                    p.question.desc = q.desc;

                                data.data.push(p);
                            }
                        });
                    });

                    // sort by replies' dateReplied in increasing order
                    data.data.sort(function (a, b) {
                        return b.dateCreated - a.dateCreated;
                    });

                    data.count = data.data.length;
                    if (data.count) {
                        data.nextTs = data.data[0].dateCreated.getTime();
                        data.lastBy = data.data[0].user;
                    }

                    callback(null, data);
                }
            })
        }
    });
};

/**
 * internal
 * 朋友圈 - latest replies for my liked since ts
 *  1 number of latest replies &
 *  2 last user replied
 * @param user
 * @param ts
 * @param callback
 */
exports.getLatestReplyOfMyLikedService = function (user, ts, callback) {
    var uid = mongoose.Types.ObjectId(user._id);

    // 我点赞的是否有新回复
    questionPraise.aggregate([
            {$match: {user: uid}},
            {$group: {_id: '$question'}}
        ])
        .exec(function (err, praises) {

            if (err || praises == null || praises.length == 0) {
                callback(err, null);
            } else {
                var ids = praises.map(function (q) {
                    return q._id;
                });

                // TODO: may need to break the ids into batches
                var matchOpt = {
                    isValid: true,
                    category: {$in: ['share', 'welcome']},
                    dateLastModified: {$gt: new Date(Number(ts))},
                    createdBy: {$ne: uid}, // not created by myself
                    _id: {$in: ids}
                };

                exports.getLatestReplyOfMyParticipatedService(user, ts, matchOpt, callback);

                // need to filter out the replies before the time I clicked "Like"
            }
        });
};

/**
 * internal
 * 朋友圈 - likes for my participated since ts
 *  1 number of latest likes &
 *  2 last user who liked
 * @param user
 * @param ts
 * @param callback
 */
exports.getLatestLikesOfMyParticipatedService = function (user, ts, callback) {
    var uid = mongoose.Types.ObjectId(user._id);

    // 参与的是否被点赞
    // TODO: maybe should be limited to a period of time
    Question.find({
            isValid: true,
            // community: cid,  // TODO: remove ?
            category: {$in: ['share', 'welcome']},
            $or: [{createdBy: uid}, {'replies.repliedBy': uid}]
        }, '_id images')
        // .sort({_id: 1})
        .exec(function (err, results) {
            if (err || results == null || results.length == 0) {
                callback(err, null);
            } else {
                var ids = results.map(function (q) {
                    return q._id;
                });

                // TODO: findOne or find with $in? query size is limited to 16MB
                // TODO: need to check the time "Liked" and then filter out the "likes" before that
                questionPraise.find({
                        question: {$in: ids},
                        dateCreated: {$gt: new Date(Number(ts))},
                        user: {$ne: user._id}   // exclude user's own like
                    })
                    .sort('-dateCreated')
                    .populate('user', 'nickName images userType authenticationStatus')
                    .populate('question', 'images desc')
                    .exec(function (err, praises) {
                        if (err) {
                            callback(err, null);
                        } else {
                            var data = {
                                count: 0,
                                nextTs: Number(ts),
                                data: []
                            };

                            if (praises && praises.length) {
                                data.count = praises.length;
                                data.lastBy = praises[0].user;
                                data.nextTs = praises[0].dateCreated.getTime();
                                data.data = praises.map(function (q) {
                                    var p = {
                                        action: 'praise',
                                        _id: q._id,
                                        dateCreated: q.dateCreated,
                                        desc: '❤️',
                                        user: q.user,
                                        question: {
                                            _id: q.question._id
                                        }
                                    };

                                    if (q.question.images && q.question.images.length > 0)
                                        p.question.images = q.question.images[0];
                                    else
                                        p.question.desc = q.question.desc;

                                    return p;
                                });
                            }
                            callback(null, data);
                        }
                    })
            }
        });
};

/**
 * internal
 * 朋友圈 - likes for my liked since ts
 *  1 number of latest likes &
 *  2 last user liked
 * @param user
 * @param ts
 * @param callback
 */
exports.getLatestLikesOfMyLikedService = function (user, ts, callback) {
    var uid = mongoose.Types.ObjectId(user._id);

    // 我点赞的是否有点赞
    // 点赞之后的点赞通知
    questionPraise.aggregate([
            {$match: {user: uid}},
            {$group: {_id: '$question'}},  // uid, question pair should be unique actually
            // {$sort: {_id: 1}}
        ])
        .exec(function (err, praises) {
            if (err || praises == null || praises.length == 0) {
                callback(err, null);
            } else {

                var ids = praises.map(function (q) {
                    return q._id;
                });

                // TODO: findOne or find with $in? query size is limited to 16MB
                // TODO: need qid & dateCreated pair to make sure the action is after user's own action
                //      since we need to find all likes after the user's.
                // TODO: need to check the time "Liked" and then filter out the "likes" before that
                questionPraise.find({
                        question: {$in: ids},
                        dateCreated: {$gt: new Date(Number(ts))},
                        user: {$ne: user._id}  // exclude user's own like
                    })
                    .sort('-dateCreated')
                    .populate('user', 'nickName images userType authenticationStatus')
                    .populate('question', 'images desc createdBy')
                    .exec(function (err, praises) {
                        if (err) {
                            callback(err, null);
                        } else {
                            var data = {
                                count: 0,
                                nextTs: Number(ts),
                                data: []
                            };

                            if (praises && praises.length) {
                                data.count = praises.length;
                                data.lastBy = praises[0].user;
                                data.nextTs = praises[0].dateCreated.getTime();
                                data.data = praises.map(function (q) {

                                    var p = {
                                        action: 'praise',
                                        _id: q._id,
                                        dateCreated: q.dateCreated,
                                        desc: '❤️',
                                        user: q.user,
                                        question: {
                                            _id: q.question._id
                                        }
                                    };

                                    if (q.question.images && q.question.images.length > 0)
                                        p.question.images = q.question.images[0];
                                    else
                                        p.question.desc = q.question.desc;

                                    return p;
                                });
                            }
                            callback(null, data);
                        }
                    })
            }
        })
};

/**
 * 朋友圈 - 我的消息列表
 *      1. 参与的(发布,回复,点赞)
 *      2.
 * @param page
 * @param token
 * @param callback
 */
exports.getMyMessageListService = function (user, ts, callback) {

    var uid = mongoose.Types.ObjectId(user._id);

    async.parallel([
            function (cb) {
                // ts = (ts == '0') ? new Date() : new Date(Number(ts));
                var matchOpt = {
                    isValid: true,
                    // community: cid,  // TODO: remove ?
                    dateLastModified: {$gt: new Date(Number(ts))},
                    category: {$in: ['share', 'welcome']},
                    $or: [{createdBy: uid}, {'replies.repliedBy': uid}]
                };

                exports.getLatestReplyOfMyParticipatedService(user, ts, matchOpt, cb);
            },
            function (cb) {
                exports.getLatestReplyOfMyLikedService(user, ts, cb);
            },
            function (cb) {
                exports.getLatestLikesOfMyParticipatedService(user, ts, cb);
            },
            function (cb) {
                exports.getLatestLikesOfMyLikedService(user, ts, cb);
            },
        ],
        // callback
        function (err, result) {

            if (err || result == null || result.length == 0) {
                callback(err, null);
            } else {
                var data = {
                    count: 0,
                    nextTs: Number(ts),
                    data: []
                };

                for (var i = 0; i < result.length; i++) {
                    if (result[i] == null) continue;

                    data.count += result[i].count;
                    if (data.nextTs < result[i].nextTs) {
                        data.nextTs = result[i].nextTs;
                    }
                    if (result[i].data)
                        data.data = data.data.concat(result[i].data);
                }

                // 去重
                data.data = data.data.slice()
                    .sort(function (a, b) {
                        return (a._id > b._id) ? 1 : (b._id > a._id) ? -1 : 0;
                    })
                    .reduce(function (a, b) {
                        if (a.length == 0) {
                            a.push(b);
                            return a;
                        }
                        if (!a.slice(-1)[0]._id.equals(b._id)) a.push(b);
                        // else
                        //     console.log("likes(obj): ", b);
                        return a;
                    }, []);

                data.data.sort(function (q1, q2) {
                    return q2.dateCreated - q1.dateCreated
                });

                callback(null, data);
            }
        }
    );
};

